package districting;

import java.awt.Color;
import java.io.IOException;
import java.util.*;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;

public class State extends Zone implements Colorable {

   private String name;

   private Voronoi myVor;

   private int id;

   public State(String name, int representatives, Color color, int id) {
      this.name = name;
      this.numDivisions = representatives;
      this.id = id;
      this.booths = new HashSet<PollingBooth>();
      this.divisions = new HashMap<DivisionMethod, List<Division>>();
      for (DivisionMethod method: DivisionMethod.values()) {
      divisions.put(method, new ArrayList<Division>());
      }
      this.color = color;
      this.minLat = Float.MAX_VALUE;
      this.minLong = Float.MAX_VALUE;
      this.maxLat = -Float.MAX_VALUE;
      this.maxLong = -Float.MAX_VALUE;
      pbGeometry = new GeometryFactory().createMultiPoint(new Coordinate[] {});
      myVor = new Voronoi();
   }

   /**
    * @return the name
    */
   public String getName() {
      return name;
   }

   /**
    * @param name
    *           the name to set
    */
   public void setName(String name) {
      this.name = name;
   }

   /**
    * @return the id
    */
   public int getId() {
      return id;
   }

   /**
    * @param id
    *           the id to set
    */
   public void setId(int id) {
      this.id = id;
   }

   public void printAllDivisionsNames(DivisionMethod divMethod) {
      for (Division d : divisions.get(divMethod)) {
         System.out.println(d.getName());
      }
   }

   /**
    * @return the color
    */
   public Color getColor() {
      return color;
   }

   /**
    * @param color
    *           the color to set
    */
   public void setColor(Color color) {
      this.color = color;
   }

   public void collateResults(DivisionMethod divMethod) {
      votingPopulation = 0;
      for (Division d : divisions.get(divMethod)) {
         d.collate2PP();
         votingPopulation += d.getVotingPopulation();
      }
   }

   public void print2PPResult(DivisionMethod divMethod) {
      System.out.println(name);
      if (divisions.get(divMethod) != null) {
         for (Division d : divisions.get(divMethod)) {
            d.print2PPResult();
         }
      }
   }

   public Set<Division> getLaborWins(DivisionMethod divMethod) {
      Set<Division> laborWins = new HashSet<Division>();
      for (Division division : divisions.get(divMethod)) {
         if (division.predictedLabor()) {
            laborWins.add(division);
         }
      }
      return laborWins;
   }

   public Set<Division> getCoalitionWins(DivisionMethod divMethod) {
      Set<Division> coalitionWins = new HashSet<Division>();
      for (Division division : divisions.get(divMethod)) {
         if (division.predictedCoalition()) {
            coalitionWins.add(division);
         }
      }
      return coalitionWins;
   }

   public void setPolygons() {
      for (PollingBooth pb : booths) {
         if (pb.getPoint() != null) {
            myVor.addPoint(pb.getPoint().getCoordinate());
         }
      }
      System.out.println(name);
      Geometry areas = myVor.generateVoronoi(minLong, maxLong, minLat, maxLat)
            .intersection(area);
      System.out.println(areas.getNumGeometries());

      // TODO - This function is n^2 where n is the number of polling booths.
      // That is very slow - we need to try to speed it up.
      for (PollingBooth pb : booths) {
         if (pb.getPoint() != null) {
            Boolean found = false;
            for (int i = 0; i < areas.getNumGeometries() && !found; i++) {
               Geometry polygon = areas.getGeometryN(i);
               if (polygon.intersects(pb.getPoint())) {
                  pb.setPolygon(polygon);
                  found = true;
               }
            }
            if (!found) {
               System.out.println("Could not find an area for " + pb.getName());
            }
         }
      }
   }

   public void initGeometry() {
      Coordinate[] coordinates = new Coordinate[booths.size()];

      int i = 0;
      for (PollingBooth pb : booths) {
         coordinates[i] = pb.getPoint().getCoordinate();
         i++;
      }

      pbGeometry = new GeometryFactory().createMultiPoint(coordinates);
   }

   /**
    * Returns the state's boundary. Currently does this using convex hulls.
    * Hopefully I can find I better way to do this.
    * 
    * @return
    */
   public Geometry getStateArea() {
      area = pbGeometry.convexHull();
      return area;
   }

   public boolean containsPoint(Point point) {
      return area.contains(point);
   }

}